package org.foreverframework.office;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.ImageIcon;

import jp.ne.so_net.ga2.no_ji.jcom.IDispatch;
import jp.ne.so_net.ga2.no_ji.jcom.JComException;
import jp.ne.so_net.ga2.no_ji.jcom.ReleaseManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MSWordUtil {

	private static final String COLUMNS = "Columns";
	private static final String CELLS = "Cells";
	private static final String ROWS = "Rows";
	private static final String COUNT = "Count";
	private static final String TABLES = "Tables";
	private static final String ITEM = "Item";
	private static final String CELL = "Cell";
	private static final String RANGE = "Range";
	private static final String TOP = "Top";
	private static final String LEFT = "Left";
	private static final String Z_ORDER = "ZOrder";
	private static final String CONVERT_TO_SHAPE = "ConvertToShape";
	private static final String HEIGHT = "Height";
	private static final String WIDTH = "Width";
	private static final String ADD_PICTURE = "AddPicture";
	private static final String INLINE_SHAPES = "InlineShapes";
	private static final String MATCH_ALL_WORD_FORMS = "MatchAllWordForms";
	private static final String MATCH_SOUNDS_LIKE = "MatchSoundsLike";
	private static final String MATCH_WILDCARDS = "MatchWildcards";
	private static final String MATCH_BYTE = "MatchByte";
	private static final String MATCH_WHOLE_WORD = "MatchWholeWord";
	private static final String MATCH_CASE = "MatchCase";
	private static final String FORMAT = "Format";
	private static final String FORWARD = "Forward";
	private static final String EXECUTE = "Execute";
	private static final String FALSE = "False";
	private static final String TRUE = "True";
	private static final String CLEAR_FORMATTING = "ClearFormatting";
	private static final String SAVE_AS = "saveAs";
	private static final String FIND = "Find";
	private static final String HOME_KEY = "HomeKey";
	private static final String TEXT = "Text";
	private static final String SELECTION = "Selection";
	private static final String DOCUMENTS = "Documents";
	private static final String VISIBLE = "Visible";
	private static final String OPEN = "Open";
	private static final String QUIT = "Quit";
	private static final String CLOSE = "Close";
	private static final int wdFormatPDF = 17;
	private static final int wdDoNotSaveChanges = 0;
	protected static final Integer UNIT_WDSTORY = new Integer(6);// 移动光标至文档开始
	protected static final Integer UNIT_WDLINE = new Integer(5);// 选择当前行

	private static Logger log = LoggerFactory.getLogger(MSWordUtil.class);
	public static final String WORD_APP = "Word.Application";
	protected IDispatch doc;
	protected Map<String, IDispatch> docs;
	protected IDispatch documents;
	protected String newFileName;
	protected static ReleaseManager rm;
	protected IDispatch word;

	static {
		log.info("加载jcom.dll");
		URL url = MSWordUtil.class.getClassLoader().getResource("jcom.dll");
		String filePath = url.getFile();
		log.info("jcom.dll文件存放路径：" + filePath);
		System.load(filePath);
		rm = new ReleaseManager();
	}

	public MSWordUtil() {
		try {
			word = new IDispatch(rm, WORD_APP);
			word.put(VISIBLE, new Boolean(true));
			documents = (IDispatch) word.get(DOCUMENTS);
			docs = new HashMap<String, IDispatch>();
		} catch (JComException e) {
			throw new RuntimeException("初始化word程序出错。服务器没有安装word程序");
		}
	}

	public IDispatch open(String fileName) throws Exception {
		File file = new File(fileName);
		if (file.exists()) {
			doc = (IDispatch) documents.method(OPEN, new Object[] { fileName });
			docs.put(fileName, doc);
		} else {
			throw new Exception("文件不存在：" + fileName);
		}
		return doc;
	}

	public void closeDoc(IDispatch doc, boolean saveOnExit) throws Exception {
		doc.method(CLOSE, new Object[] { new Boolean(saveOnExit) });
	}

	public void closeAllDoc(boolean saveOnExit) throws Exception {
		for (IDispatch doc : docs.values()) {
			doc.method(CLOSE, new Object[] { new Boolean(saveOnExit) });
		}
	}

	public void quit(int wdDoNotSaveChanges) throws Exception {
		word.method(QUIT, new Object[] { wdDoNotSaveChanges });
	}

	public void quit() throws Exception {
		word.method(QUIT, new Object[] { wdDoNotSaveChanges });
	}

	protected IDispatch getSelection() throws Exception {
		return (IDispatch) word.get(SELECTION);
	}

	protected boolean find(String findText) throws Exception {
		IDispatch find = (IDispatch) getSelection().get(FIND);
		find.method(CLEAR_FORMATTING, null);
		find.put(TEXT, findText);
		find.put(FORWARD, TRUE);
		find.put(FORMAT, FALSE);
		find.put(MATCH_CASE, TRUE);
		find.put(MATCH_WHOLE_WORD, FALSE);
		find.put(MATCH_BYTE, TRUE);
		find.put(MATCH_WILDCARDS, FALSE);
		find.put(MATCH_SOUNDS_LIKE, FALSE);
		find.put(MATCH_ALL_WORD_FORMS, FALSE);
		return new Boolean(find.method(EXECUTE, null).toString());
	}

	public boolean replace(String findText, String newText) throws Exception {
		boolean leaf = false;
		if (find(findText)) {
			getSelection().put(TEXT, newText);
			getSelection().method(HOME_KEY, new Object[] { UNIT_WDSTORY });
			leaf = true;
		}
		return leaf;
	}

	public boolean replaceAll(String findText, String replaceText)
			throws Exception {
		boolean leaf = false;
		while (find(findText)) {
			getSelection().put(TEXT, replaceText);
			getSelection().method(HOME_KEY, new Object[] { UNIT_WDSTORY });
			leaf = true;
		}
		return leaf;
	}

	protected void replaceImage(IDispatch selection, String imagePath)
			throws Exception {
		IDispatch inlineShapes = (IDispatch) selection.get(INLINE_SHAPES);
		inlineShapes.method(ADD_PICTURE, new Object[] { imagePath });
	}

	protected void replaceImage(IDispatch selection, String imagePath,
			int ZOrder, int width, int height) throws Exception {
		IDispatch inlineShapes = (IDispatch) selection.get(INLINE_SHAPES);
		IDispatch image = (IDispatch) inlineShapes.method(ADD_PICTURE,
				new Object[] { imagePath });
		if (width == 0 && height == 0) {
			ImageIcon imageIcon = new ImageIcon(imagePath);
			width = imageIcon.getIconWidth();
			height = imageIcon.getIconHeight();
		}
		image.put(WIDTH, width);
		image.put(HEIGHT, height);
		IDispatch shape = null;
		try {
			Thread.sleep(500);
			shape = (IDispatch) image.method(CONVERT_TO_SHAPE, null);
		} catch (Exception e) {
			throw new Exception("插入图片出错");
		}
		shape.method(Z_ORDER, new Object[] { ZOrder });
		Float left = Float.parseFloat(shape.get(LEFT).toString());
		Float top = Float.parseFloat(shape.get(TOP).toString());
		shape.method("IncrementTop", new Object[] { -height / 2 });
		log.info("left=" + left + ":top=" + top);
	}

	public boolean replaceImage(String toFindText, String imagePath)
			throws Exception {
		boolean leaf = false;
		if (find(toFindText)) {
			replaceImage(getSelection(), imagePath);
			getSelection().method(HOME_KEY, new Object[] { UNIT_WDSTORY });
			leaf = true;
		}
		return leaf;
	}

	/**
	 * 查找toFindText，替换成指定的图片
	 * 
	 * @param toFindText
	 *            - String 欲替换的文本
	 * @param imagePath
	 *            - String 图片文件全路径
	 * @param ZOrder
	 *            - String 图片版式 4浮于文字上方，5浮于文字下方
	 * @return
	 * @throws Exception
	 */
	public boolean replaceImage(String toFindText, String imagePath,
			int ZOrder, int width, int height) throws Exception {
		boolean leaf = false;
		if (find(toFindText)) {
			replaceImage(getSelection(), imagePath, ZOrder, width, height);
			getSelection().method(HOME_KEY, new Object[] { UNIT_WDSTORY });
			leaf = true;
		}
		return leaf;
	}

	/**
	 * 查找toFindText，替换成指定的图片
	 * 
	 * @param toFindText
	 *            - String 欲替换的文本
	 * @param imagePath
	 *            - String 图片文件全路径
	 * @param ZOrder
	 *            - String 图片版式 4浮于文字上方，5浮于文字下方
	 * @return
	 * @throws Exception
	 */
	public boolean replaceAllImage(String toFindText, String imagePath,
			int ZOrder, int width, int height) throws Exception {
		boolean leaf = false;
		while (find(toFindText)) {
			replaceImage(getSelection(), imagePath, ZOrder, width, height);
			getSelection().method("HomeKey", new Object[] { new Integer(6) });
			leaf = true;
		}
		return leaf;
	}

	/**
	 * 查找toFindText，并替换为指定的word文件内容
	 * 
	 * @param toFindText
	 *            - String 欲替换的文本
	 * @param docFile
	 *            - String doc文件的全路径
	 * @throws Exception
	 */
	public boolean replaceFile(String toFindText, String docFile)
			throws Exception {
		boolean leaf = false;
		if (find(toFindText)) {
			replaceFile(getSelection(), docFile);
			getSelection().method("HomeKey", new Object[] { new Integer(6) });
			leaf = true;
		}
		return leaf;
	}

	protected void replaceFile(IDispatch selection, String docFile)
			throws Exception {
		selection.method("insertFile", new Object[] { docFile, "",
				new Boolean(false), new Boolean(false), new Boolean(false) });
		selection.method("TypeBackspace", null);
	}

	/**
	 * 查找toFindText，并替换为指定的word文件内容
	 * 
	 * @param toFindText
	 *            - String 欲替换的文本
	 * @param docFile
	 *            - String doc文件的全路径
	 * @throws Exception
	 */
	public boolean replaceAllFile(String toFindText, String docFile)
			throws Exception {
		boolean leaf = false;
		while (find(toFindText)) {
			replaceFile(getSelection(), docFile);
			getSelection().method("HomeKey", new Object[] { new Integer(6) });
			leaf = true;
		}
		return leaf;
	}

	protected void insertTable(IDispatch range, List<Object[]> dataList)
			throws Exception {
		IDispatch table;
		if (dataList.size() < 1) {
			System.out.println("Empty table!");
			return;
		}
		Object titleData[] = dataList.get(0);
		int rowLength = dataList.size();
		int colLength = titleData.length;
		IDispatch tables = (IDispatch) doc.get(TABLES);
		table = (IDispatch) tables.method("Add", new Object[] { range,
				rowLength, colLength });
		for (int i = 1; i <= rowLength; i++) {
			Object datas[] = dataList.get(i - 1);
			for (int j = 1; j <= datas.length; j++) {
				IDispatch cell = (IDispatch) table.method(CELL, new Object[] {
						new Integer(i), new Integer(j) });
				IDispatch cellRange = (IDispatch) cell.get(RANGE);
				cellRange.put("Text", datas[j - 1]);
			}
		}
	}

	/**
	 * 在指定行后面添加count行
	 * 
	 * @param tbIndex
	 *            指定表格
	 * @param rowIndex
	 *            第几行
	 * @param count
	 *            添加几行
	 * @return
	 * @throws Exception
	 */
	public boolean insertTabRow(int tbIndex, int rowIndex, int colIndex,
			int count) throws Exception {
		IDispatch cell = getTabCell(tbIndex, rowIndex, colIndex);
		IDispatch range = (IDispatch) cell.get(RANGE);
		range.method("select", null);
		getSelection().method("InsertRowsBelow", new Object[] { count });
		return true;
	}

	public boolean insertTabRow(int tbIndex) throws Exception {
		IDispatch rows = getRows(tbIndex);
		rows.method("Add", null);
		return true;
	}

	/**
	 * 填充表格数据 如果输入的数据宽度(列数)超出模板设置，则超出的部分不会被插入
	 * 
	 * @throws Exception
	 */
	public void replaceTable(int tbIndex, int fromRow, int fromCol,
			List<Object[]> dataList) throws Exception {
		if (dataList.size() < 1) {
			System.out.println("Empty table!");
			return;
		}
		IDispatch rows = getRows(tbIndex);
		int rowsCount = getTabRowCount(tbIndex);
		int columnsCount = getTabColCount(tbIndex);

		if (fromRow > rowsCount) {
			for (int i = 0; i < fromRow - rowsCount; i++) {
				rows.method("Add", null);
			}
		} else if (fromRow == -1) {
			fromRow = rowsCount;
		}

		int rowSum = 0;
		for (int i = fromRow, index = 0; index < dataList.size(); i++, index++) {
			Object datas[] = dataList.get(index);

			List<List<?>> rowChildList = new ArrayList<List<?>>();
			List<Integer[]> list = new ArrayList<Integer[]>();
			for (int j = fromCol, h = 0; h < datas.length && j <= columnsCount; j++, h++) {
				Object item = datas[h];
				int rowChildColSum = 0;
				for (Object object : rowChildList) {
					Object[] array = ((Object[]) ((List<?>) object).get(0));
					if (array.length > 1) {
						rowChildColSum += array.length - 1;
					} else {
						rowChildColSum += 1;
					}
				}
				if (item instanceof java.lang.String) {
					setCellText(tbIndex, i + rowSum, (rowChildColSum > 0 ? j
							+ rowChildColSum : j), datas[h] == null ? ""
							: datas[h].toString());
				} else if (item instanceof java.util.List<?>) {
					List<?> childDataList = (List<?>) item;

					if (childDataList.size() == 0)
						continue;

					int numRows = childDataList.size();
					int numColumns = 0;
					int lastRowMergeNum = 0;
					for (Object object : childDataList) {
						Object[] dataA = (Object[]) object;
						if (dataA.length > numColumns) {
							numColumns = dataA.length;
						}
						lastRowMergeNum = dataA.length;
					}

					splitCell(tbIndex, i + rowSum, (rowChildColSum > 0 ? j
							+ rowChildColSum : j), numRows, numColumns);

					list.add(new Integer[] { j, lastRowMergeNum });
					for (int k = 0; k < childDataList.size(); k++) {
						Object[] dataA = (Object[]) childDataList.get(k);
						if (dataA.length < numColumns) {
							rowChildColSum = 0;
							for (Object object : rowChildList) {
								rowChildColSum += ((Object[]) ((List<?>) object)
										.get(k)).length - 1;
							}
							int form_Col = rowChildColSum > 0 ? j
									+ rowChildColSum : j;
							mergeCell(tbIndex, i + rowSum + k, form_Col, i
									+ rowSum + k, form_Col + numColumns
									- dataA.length);
						}
					}
					for (int k = 0; k < childDataList.size(); k++) {
						Object[] dataA = (Object[]) childDataList.get(k);
						rowChildColSum = 0;
						for (Object object : rowChildList) {
							rowChildColSum += ((Object[]) ((List<?>) object)
									.get(k)).length - 1;
						}
						for (int l = 0; l < dataA.length; l++) {
							setCellText(tbIndex, i + rowSum + k, l
									+ (rowChildColSum > 0 ? j + rowChildColSum
											: j), dataA[l].toString());
						}
					}
					rowChildList.add(childDataList);
				} else {
					throw new Exception("传入的集合格式不正确!");
				}
			}
			int maxRowSum = 0;
			for (Object item : datas) {
				if (item instanceof java.util.List<?>) {
					if (maxRowSum < ((List<?>) item).size()) {
						maxRowSum = ((List<?>) item).size();
					}
				}
			}
			rowSum += (maxRowSum > 1 ? maxRowSum - 1 : 0);
			if (index + 1 != dataList.size()) {
				rows.method("Add", null);
				for (Integer[] array : list) {
					mergeCell(tbIndex, i + rowSum + 1, array[0],
							i + rowSum + 1, array[0] + array[1] - 1);
				}
			}
		}
	}

	/**
	 * 拆分指定单元格为指定的几行几列
	 * 
	 * @param tbIndex
	 * @param fromRow
	 * @param fromCol
	 * @param numRows
	 * @param numColumns
	 * @throws Exception
	 */
	public void splitCell(int tbIndex, int fromRow, int fromCol, int numRows,
			int numColumns) throws Exception {
		IDispatch cell = getTabCell(tbIndex, fromRow, fromCol);
		cell.method("split", new Object[] { numRows, numColumns });
	}

	/**
	 * 合并单元格
	 * 
	 * @param tbIndex
	 * @param fromRow
	 * @param fromCol
	 * @param toRow
	 * @param toCol
	 * @throws Exception
	 */
	public void mergeCell(int tbIndex, int fromRow, int fromCol, int toRow,
			int toCol) throws Exception {
		int rowsCount = getTabRowCount(tbIndex);
		if (fromRow < 1 || fromRow > rowsCount)
			fromRow = 1;
		if (toRow < 1 || toRow > rowsCount)
			toRow = rowsCount;
		int Columnscount = getTabColCount(tbIndex);
		if (fromCol < 1 || fromCol > Columnscount)
			fromCol = 1;
		if (toCol < 1 || toCol > Columnscount)
			toCol = Columnscount;
		IDispatch fromCell = getTabCell(tbIndex, fromRow, fromCol);
		IDispatch toCell = getTabCell(tbIndex, toRow, toCol);
		fromCell.method("Merge", new Object[] { toCell });
	}

	/**
	 * 填充指定单元格文本内容
	 * 
	 * @param tbIndex
	 * @param row
	 * @param col
	 * @param text
	 * @throws Exception
	 */
	public void setCellText(int tbIndex, int row, int col, String text)
			throws Exception {
		IDispatch cell = null;
		try {
			cell = getTabCell(tbIndex, row, col);
		} catch (Exception e) {
			throw new Exception("获取单元格：" + row + "行" + col + "列失败");
		}
		IDispatch cellRange = (IDispatch) cell.get(RANGE);
		cellRange.put("Text", text);
	}

	protected IDispatch getRows(int tbIndex) throws Exception {
		IDispatch table = getTable(tbIndex);
		return (IDispatch) table.get(ROWS);
	}

	protected IDispatch getCloumn(int tbIndex) throws Exception {
		IDispatch table = getTable(tbIndex);
		return (IDispatch) table.get(COLUMNS);
	}

	/**
	 * 获取指定表格有多少列
	 * 
	 * @param tbIndex
	 *            - int 表格索引
	 * @return 返回该表格由多少列组成
	 * @throws Exception
	 */
	public int getTabColCount(int tbIndex) throws Exception {
		IDispatch Columns = (IDispatch) getTable(tbIndex).get(COLUMNS);
		return (Integer) Columns.get(COUNT);
	}

	/**
	 * 获取指定表格的指定行的列数
	 * 
	 * @param tbIndex
	 * @param rowIndex
	 * @return
	 * @throws Exception
	 */
	public int getTabRowColCount(int tbIndex, int rowIndex) throws Exception {
		IDispatch row = getTabRow(tbIndex, rowIndex);
		// row.method("Select", null);
		IDispatch cells = (IDispatch) row.get(CELLS);
		return (Integer) cells.get(COUNT);
	}

	/**
	 * 获取指定表格有多少行
	 * 
	 * @param tbIndex
	 *            - int 表格索引
	 * @return 返回该表格由多少行组成
	 * @throws Exception
	 */
	public int getTabRowCount(int tbIndex) throws Exception {
		IDispatch rows = (IDispatch) getTable(tbIndex).get(ROWS);
		return (Integer) rows.get(COUNT);
	}

	/**
	 * 获取指定索引的表格对象
	 * 
	 * @param tbIndex
	 * @return
	 * @throws Exception
	 */
	protected IDispatch getTable(int tbIndex) throws Exception {
		IDispatch tables = (IDispatch) doc.get(TABLES);
		try {
			return (IDispatch) tables.method(ITEM, new Object[] { tbIndex });
		} catch (Exception e) {
			throw new Exception("索引为：" + tbIndex + "的表格不存在！");
		}
	}

	protected IDispatch getTabRow(int tbIndex, int rowIndex) throws Exception {
		IDispatch rows = getRows(tbIndex);
		IDispatch row = (IDispatch) rows
				.method(ITEM, new Object[] { rowIndex });
		return row;
	}

	/**
	 * 删除指定表格的指定行
	 * 
	 * @param tbIndex
	 *            - int 表格索引
	 * @param rowIndex
	 *            - int 表格行索引
	 * @throws Exception
	 */
	public void delTabRow(int tbIndex, int rowIndex) throws Exception {
		IDispatch row = getTabRow(tbIndex, rowIndex);
		row.method("Select", null);
		row.method("Delete", null);
	}

	protected IDispatch getTabCell(int tbIndex, int row, int col)
			throws Exception {
		return (IDispatch) getTable(tbIndex).method(CELL,
				new Object[] { new Integer(row), new Integer(col) });
	}

	/**
	 * 判断指定行是否是空行，就是该行的每个单元格内容为空
	 * 
	 * @param tbIndex
	 * @param rowIndex
	 * @return
	 * @throws Exception
	 */
	public boolean rowIsEmpty(int tbIndex, int rowIndex) throws Exception {
		int cols = getTabColCount(tbIndex);
		for (int i = 1; i <= cols; i++) {
			IDispatch cell = getTabCell(tbIndex, rowIndex, i);
			IDispatch cellRange = (IDispatch) cell.get(RANGE);
			String cellText = cellRange.get(TEXT).toString();
			if (!"\r".equals(cellText)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 获取word中指定表格的数据，以java集合形式返回
	 * 
	 * @param i
	 * @return
	 * @throws Exception
	 */
	public List<String[]> tableToList(int tbIndex) throws Exception {
		List<String[]> dataList = new ArrayList<String[]>();
		int rows = getTabRowCount(tbIndex);
		int cols = getTabColCount(tbIndex);
		for (int i = 1; i <= rows; i++) {
			String[] rowItem = new String[cols];
			for (int j = 1; j <= cols; j++) {
				IDispatch cell = getTabCell(tbIndex, i, j);
				IDispatch range = (IDispatch) cell.get(RANGE);
				rowItem[j - 1] = range.get(TEXT).toString();
			}
			dataList.add(rowItem);
		}
		return dataList;
	}

	public boolean savePDF(String inputFile, String outputFile)
			throws Exception {
		IDispatch doc = open(inputFile);
		doc.method(SAVE_AS, new Object[] { outputFile, wdFormatPDF });
		return true;
	}

	public boolean savePDF(String outputFile) throws Exception {
		doc.method(SAVE_AS, new Object[] { outputFile, wdFormatPDF });
		return true;
	}

	public void saveDoc(String outputFile) throws Exception {
		doc.method(SAVE_AS, new Object[] { outputFile, new Integer(0) });
	}

	protected void moveDown(IDispatch selection, int count) throws Exception {
		for (int i = 0; i < count; i++) {
			selection.method("MoveDown", null);
		}
	}

	protected void moveDown(int count) throws Exception {
		moveDown(getSelection(), count);
	}

	protected void moveLeft(IDispatch selection, int count) throws Exception {
		for (int i = 0; i < count; i++) {
			selection.method("MoveLeft", null);
		}
	}

	protected void moveLeft(int count) throws Exception {
		moveLeft(getSelection(), count);
	}

	protected void moveRight(IDispatch selection, int count) throws Exception {
		for (int i = 0; i < count; i++) {
			selection.method("MoveRight", null);
		}
	}

	protected void moveStart(IDispatch selection) throws Exception {
		selection.method("HomeKey", new Object[] { 6 });
	}

	protected void moveUp(IDispatch selection, int count) throws Exception {
		for (int i = 0; i < count; i++) {
			selection.method("MoveUp", null);
		}
	}

	protected void moveUp(int count) throws Exception {
		moveUp(getSelection(), count);
	}
}
